package onez.project.core.helper;

import onez.project.common.config.*;
import onez.project.common.constant.BasicConst;
import onez.project.common.constant.GatewayConst;
import onez.project.common.exception.ResponseException;
import onez.project.core.context.GatewayContext;
import onez.project.core.request.GatewayRequest;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import org.apache.commons.lang3.StringUtils;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

import static onez.project.common.enums.ResponseCode.PATH_NO_MATCHED;



public class RequestHelper {
    public static GatewayContext doContext(FullHttpRequest request, ChannelHandlerContext ctx) {

        //	构建请求对象GatewayRequest
        GatewayRequest gateWayRequest = doRequest(request, ctx);

        //	根据请求对象里的uniqueId，获取资源服务信息(也就是服务定义信息)
        ServiceDefinition serviceDefinition =
                DynamicConfigManager.getInstance().getServiceDefinition(gateWayRequest.getUniqueId());


        //	根据请求对象获取服务定义对应的方法调用，然后获取对应的规则
        ServiceInvoker serviceInvoker = new HttpServiceInvoker();
        serviceInvoker.setInvokerPath(gateWayRequest.getPath());
        serviceInvoker.setTimeout(500);

        //根据请求对象获取规则
        Rule rule = getRule(gateWayRequest, serviceDefinition.getServiceId());

        //	构建我们而定GateWayContext对象
        GatewayContext gatewayContext = new GatewayContext(serviceDefinition.getProtocol(), ctx, HttpUtil.isKeepAlive(request), gateWayRequest, rule,0);


        //后续服务发现做完，这里都要改成动态的--以及在负载均衡算法实现
        gatewayContext.getRequest().setModifyHost("127.0.0.1:8080");

        return gatewayContext;
    }

    /**
     * 构建Request请求对象
     *
     * @param fullHttpRequest 原始HTTP请求对象，包含请求头、方法、URI等基础信息
     * @param ctx             Netty通道上下文，用于获取客户端网络连接信息
     * @return GatewayRequest 组装完成的网关请求对象，包含：
     *                        - 服务唯一标识
     *                        - 字符编码
     *                        - 客户端真实IP
     *                        - 请求主机地址
     *                        - 请求路径
     *                        - HTTP方法
     *                        - 内容类型
     *                        - 完整请求头集合
     *                        - 原始请求对象
     */
    private static GatewayRequest doRequest(FullHttpRequest fullHttpRequest, ChannelHandlerContext ctx) {

        HttpHeaders headers = fullHttpRequest.headers();
        // 从header头获取必须要传入的关键属性 uniqueId
        String uniqueId = headers.get(GatewayConst.UNIQUE_ID);
        String host = headers.get(HttpHeaderNames.HOST);
        HttpMethod method = fullHttpRequest.method();
        String uri = fullHttpRequest.uri();
        String clientIp = getClientIp(ctx, fullHttpRequest);
        // 解析Content-Type时处理空值情况
        String contentType = HttpUtil.getMimeType(fullHttpRequest) == null ? null :
                HttpUtil.getMimeType(fullHttpRequest).toString();
        Charset charset = HttpUtil.getCharset(fullHttpRequest, StandardCharsets.UTF_8);

        return new GatewayRequest(uniqueId, charset, clientIp, host, uri, method,
                contentType, headers, fullHttpRequest);
    }

    /**
     * 获取客户端ip（支持代理转发场景）
     *
     * @param ctx     Netty通道上下文，用于直接获取客户端连接地址
     * @param request HTTP请求对象，用于获取X-Forwarded-For头信息
     * @return 客户端真实IP地址，按以下优先级获取：
     *         1. 从X-Forwarded-For头第一个有效值获取
     *         2. 直接获取通道的远程地址
     */
    private static String getClientIp(ChannelHandlerContext ctx, FullHttpRequest request) {
        String xForwardedValue = request.headers().get(BasicConst.HTTP_FORWARD_SEPARATOR);

        String clientIp = null;
        // 处理多级代理的IP串联情况
        if (StringUtils.isNotEmpty(xForwardedValue)) {
            List<String> values = Arrays.asList(xForwardedValue.split(", "));
            if (values.size() >= 1 && StringUtils.isNotBlank(values.get(0))) {
                clientIp = values.get(0);
            }
        }
        // 无代理时直接从通道获取IP
        if (clientIp == null) {
            InetSocketAddress inetSocketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
            clientIp = inetSocketAddress.getAddress().getHostAddress();
        }
        return clientIp;
    }

    /**
     * 根据请求对象获取Rule对象
     *
     * @param gateWayRequest 网关请求对象，包含请求路径等匹配要素
     * @param serviceId      当前服务ID，用于组合路径匹配键
     * @return 路由规则对象，匹配策略为：
     *         1. 优先通过【服务ID+请求路径】精确匹配
     *         2. 未匹配时通过服务ID获取所有规则，选择请求路径匹配前缀的规则
     * @throws ResponseException 当无任何规则匹配时抛出PATH_NO_MATCHED异常
     */
    private static Rule getRule(GatewayRequest gateWayRequest, String serviceId) {
        String key = serviceId + "." + gateWayRequest.getPath();
        Rule rule = DynamicConfigManager.getInstance().getRuleByPath(key);

        if (rule != null) {
            return rule;
        }
        // 路径精确匹配失败时，执行前缀匹配兜底策略
        return DynamicConfigManager.getInstance().getRuleByServiceId(serviceId).stream()
                .filter(r -> gateWayRequest.getPath().startsWith(r.getPrefix()))
                .findAny()
                .orElseThrow(() -> new ResponseException(PATH_NO_MATCHED));
    }


}
